const cfg = require('../config.json');
const pg = require('../db/db');
const Client = require('bitcoin-core');
const { logger } = require('./logger');
let client;

// init client 
let initClient = async () => {
    try {
        client = new Client(cfg.bitCfg);
        if (typeof client == 'undefined') {
            client = new Client(cfg.bitCfg);
        }
        logger.info('bitcoin client init success.');
    } catch (error) {
        logger.error("client init error===>", error);
    }
}
/**
 * test insert into db
 */
let testBlock = {
    "hash": "00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206",
    "confirmations": 1351475,
    "strippedsize": 190,
    "size": 190,
    "weight": 760,
    "height": 1,
    "version": 1,
    "versionHex": "00000001",
    "merkleroot": "f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba",
    "tx": [
        "f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"
    ],
    "time": 1296688928,
    "mediantime": 1296688928,
    "nonce": 1924588547,
    "bits": "1d00ffff",
    "difficulty": 1,
    "chainwork": "0000000000000000000000000000000000000000000000000000000200020002",
    "previousblockhash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
    "nextblockhash": "000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820"
}
/**
 * test only 
 */
let rawtransactions = {
    "txid": "e38c3f3d0c1517099c5baff83efa716831a992d4d7528bf3a99e5759bf19aa6c",
    "hash": "e38c3f3d0c1517099c5baff83efa716831a992d4d7528bf3a99e5759bf19aa6c",
    "version": 2,
    "size": 190,
    "vsize": 190,
    "locktime": 0,
    "vin": [
        {
            "txid": "216ed4f8563df4e4e1833e241c1e618f13140dbad3d14dd37e4281506496af00",
            "vout": 0,
            "scriptSig": {
                "asm": "3045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf[ALL]",
                "hex": "483045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf01"
            },
            "sequence": 4294967295
        }
    ],
    "vout": [
        {
            "value": 49.00000000,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_HASH160 826e8562eb523442c37280be7770a4d479f55faa OP_EQUAL",
                "hex": "a914826e8562eb523442c37280be7770a4d479f55faa87",
                "reqSigs": 1,
                "type": "scripthash",
                "addresses": [
                    "2N58tEMk2ZepmP8kqF91qXJFk4Zc8Mn3YRD"
                ]
            }
        },
        {
            "value": 0.99999000,
            "n": 1,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 daf8b8f6beff6af014d6f3db70cefe05994eb8c8 OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a914daf8b8f6beff6af014d6f3db70cefe05994eb8c888ac",
                "reqSigs": 1,
                "type": "pubkeyhash",
                "addresses": [
                    "n1UmZR6uDuKCduk4naJb9UoF475UB7cDj8"
                ]
            }
        }
    ],
    "hex": "020000000100af96645081427ed34dd1d3ba0d14138f611e1c243e83e1e4f43d56f8d46e210000000049483045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf01ffffffff02001110240100000017a914826e8562eb523442c37280be7770a4d479f55faa8718ddf505000000001976a914daf8b8f6beff6af014d6f3db70cefe05994eb8c888ac00000000"
}

async function insertTxs(blockInfo) {
    if (blockInfo.height != 0) {
        for (const txid of blockInfo.tx) {
            let txhash = await pg.any(`SELECT txid FROM bittransactions WHERE bittransactions."txid" = '${txid}'`);
            if (txhash.length == 0) {
                let rawtx = await client.getRawTransaction(txid, true)
                    .catch((error) => {
                        logger.error('getRawTransaction method error:==>', error);
                        pg.none(`INSERT INTO errtx(txid) VALUES ('${txid}');`);
                        global.gc();
                    });
                global.gc();
                await getSQLs(rawtx);
                await get_UTXO_SQLs(rawtx);
                logger.info(`the current block: ${blockInfo.height} txid: ${txid} write database success.`);
            } else {
                // logger.info(`the current block: ${blockInfo.height} txid: ${txid} is already exists in database.`);
            }
            global.gc();
        }
        global.gc();
    }
}
async function insertBlock(hash, blockInfo) {
    if (hash.length == 0) {
        await pg.none(wirteBlocks(blockInfo));
        logger.info(`the current block : ${blockInfo.height} tx size : ${blockInfo.tx.length} write database success.`);
        global.gc();
    } else {
        // logger.info(`the current block: ${blockInfo.height} blockHash: ${(hash[hash.length - 1]).hash} tx_size: ${blockInfo.tx.length} is already exists in database.`);
    }
}
// block info to wirte DB 
let blockToDB = async () => {
    try {
        // The hash of the block header from the most recent block on the best block chain
        let headerHash = await client.getBestBlockHash();
        let blockInfo = await client.getBlock(headerHash);
        global.gc();
        let hash = await pg.any(`SELECT hash FROM bitblock WHERE bitblock."hash" = '${headerHash}'`);
        // parallel handle
        Promise.all([insertBlock(hash, blockInfo), insertTxs(blockInfo)])
            .then((res) => {
                logger.info("parallel handle success: ");
            })
            .catch(error => logger.error("parallel handle error: ", error))
    } catch (error) {
        global.gc();
        logger.error("client init error===>", error);
    }
}

let wirteBlocks = (result) => {

    let sql = `INSERT INTO public.bitblock( hash, confirmations, strippedsize, weight, height, version, "versionHex", merkleroot, tx, "time", 
    mediantime, nonce, bits, difficulty, chainwork, previousblockhash, nextblockhash)
	VALUES ('${result.hash}', ${result.confirmations}, ${result.strippedsize}, ${result.weight}, ${result.height}, ${result.version}, '${result.versionHex}', '${result.merkleroot}',
			'${JSON.stringify(result.tx)}',${result.time},
			'${result.mediantime}', '${result.nonce}', '${result.bits}', ${result.difficulty}, '${result.chainwork}',
			'${result.previousblockhash == undefined ? "" : result.previousblockhash}', 
            '${result.nextblockhash == undefined ? "" : result.nextblockhash}');`;
    global.gc();
    return sql;
}


/**
 * test sql
 * 
 * INSERT INTO public.bitblock( hash, confirmations, strippedsize, weight, height, version, "versionHex", merkleroot, tx, "time", mediantime, nonce, bits, difficulty, chainwork, previousblockhash, nextblockhash)
	VALUES ('00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206', '1351475', 190, 190, 760, 1, 1, '00000001', 'f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba',
			'{f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba,f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba}',
			'1296688928', '1924588547', '1d00ffff', 1, '0000000000000000000000000000000000000000000000000000000200020002', '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', 
			'000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820');
 * 
 INSERT INTO public.bittransactions( txid, hash, version, size, vsize, locktime, vin, vout, hex)
	VALUES ('e38c3f3d0c1517099c5baff83efa716831a992d4d7528bf3a99e5759bf19aa6c', 'e38c3f3d0c1517099c5baff83efa716831a992d4d7528bf3a99e5759bf19aa6c',
			2, 190, 190, 0, '[
    {
      "txid": "216ed4f8563df4e4e1833e241c1e618f13140dbad3d14dd37e4281506496af00",
      "vout": 0,
      "scriptSig": {
        "asm": "3045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf[ALL]",
        "hex": "483045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf01"
      },
      "sequence": 4294967295
    }
  ]', '[
    {
      "value": 49.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_HASH160 826e8562eb523442c37280be7770a4d479f55faa OP_EQUAL",
        "hex": "a914826e8562eb523442c37280be7770a4d479f55faa87",
        "reqSigs": 1,
        "type": "scripthash",
        "addresses": [
          "2N58tEMk2ZepmP8kqF91qXJFk4Zc8Mn3YRD"
        ]
      }
    },
    {
      "value": 0.99999000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 daf8b8f6beff6af014d6f3db70cefe05994eb8c8 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914daf8b8f6beff6af014d6f3db70cefe05994eb8c888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "n1UmZR6uDuKCduk4naJb9UoF475UB7cDj8"
        ]
      }
    }
  ]', '020000000100af96645081427ed34dd1d3ba0d14138f611e1c243e83e1e4f43d56f8d46e210000000049483045022100ec0398a35cd21da9ca7a7d0b48c775961d258ebace9aa8f31f0eb797dc5d090102200423a0ee244736c6ce3b06dfbfb303836b86d1989e8120cb5d7fa3923caeb9bf01ffffffff02001110240100000017a914826e8562eb523442c37280be7770a4d479f55faa8718ddf505000000001976a914daf8b8f6beff6af014d6f3db70cefe05994eb8c888ac00000000')

 
* 
* 
*/
let getSQLs = async (txs) => {
    let sql_filed = `INSERT INTO bittransactions(txid, hash, version, size, vsize, locktime, vin, vout, hex `;
    let sql_value = `VALUES ('${txs.txid}', '${txs.hash}', ${txs.version}, ${txs.size}, ${txs.vsize}, 
    ${txs.locktime}, '${JSON.stringify(txs.vin)}', '${JSON.stringify(txs.vout)}', '${txs.hex}'`;

    if (txs.blockhash !== undefined) {
        sql_filed += `, blockhash, confirmations, "time", blocktime)`;
        sql_value += `, '${txs.blockhash}' , '${txs.confirmations}' , '${txs.time}', '${txs.blocktime}')`;
    } else {
        sql_filed += `)`;
        sql_value += `)`;
    }
    // let sql = `INSERT INTO bittransactions( 
    //     txid, hash, version, size, vsize, locktime, vin, vout, hex, blockhash, confirmations, "time", blocktime)
    //     VALUES ('${txs.txid}', '${txs.hash}', ${txs.version}, ${txs.size}, ${txs.vsize}, 
    //         ${txs.locktime}, '${JSON.stringify(txs.vin)}', '${JSON.stringify(txs.vout)}', '${txs.hex}'
    //             , '${txs.blockhash == undefined ? "" : txs.blockhash}'
    //             , '${txs.confirmations == undefined ? "" : txs.confirmations}'
    //             , '${txs.time == undefined ? "" : txs.time}'
    //             , '${txs.blocktime == undefined ? "" : txs.blocktime}')`;
    await pg.none(sql_filed + sql_value);
    global.gc();
}
/**
 * "txid": "bb515324334377e703b499cc970ac324c8c5f8c218c4dbdd2e5ef32f3bd6eff0"
 * "vout": [
    {
      "value": 49.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 eb1fe2bda1243ffe9b964c58f5a7d6c83ec0d534 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914eb1fe2bda1243ffe9b964c58f5a7d6c83ec0d53488ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "n2xBHbHHGwKk9yfZRGyVkuK2tFiAEyw2uq"
        ]
      }
    }
  ]
 */
let get_UTXO_SQLs = async (txs) => {
    for (const element of txs.vout) {
        let sql = `INSERT INTO utxos(
            txid, value, n, asm, hex, "reqSigs", type, addresses, isused)
            VALUES ('${txs.txid}', '${element.value}',
            ${element.n}, '${element.scriptPubKey.asm}', 
                    '${element.scriptPubKey.hex}', '${element.scriptPubKey.reqSigs == undefined ? "" : element.scriptPubKey.reqSigs}', 
                    '${element.scriptPubKey.type}', '${element.scriptPubKey.addresses == undefined ? "" : element.scriptPubKey.addresses}', null);`;
        await pg.none(sql);
        global.gc();
    }
}

// getBitTransanctions by address
let getBitTransanctions = async (transactionHash) => {
    try {
        // TODO
        // getBitTransanctions by address
        return await pg.any(`SELECT * FROM bittransactions WHERE bittransactions."txid" = '${transactionHash}'`);
        // success

    } catch (error) {
        logger.error("getBitTransanctions error===>", error);
    }
}
let isExists = () => {
    return new Promise(async (res, rej) => {
        let headerHash = await client.getBestBlockHash();
        let blockInfo = await client.getBlock(headerHash);
        logger.info("latest block is :", blockInfo.height);
        let hash = await pg.any(`SELECT hash FROM bitblock WHERE bitblock."hash" = '${headerHash}'`)
        global.gc();
        if (hash.length == 0) {
            res(true);
        } else {
            logger.info(`the current block: ${blockInfo.height} blockHash: ${(hash[hash.length - 1]).hash} tx_size: ${blockInfo.tx.length} is already exists in database.`);
            res(false)
        }
    });
}

(function timer() {
    global.gc();
    console.log('timer start...');
    if (client == undefined) {
        initClient();
    }
    isExists()
        .then(res => {
            if (res) {
                logger.info(`start write block into database...`);
                // write DB 
                blockToDB();
                global.gc();  // do something here
            }
            setTimeout(timer, cfg.timer);
        })
        .catch(error => {
            logger.info(`the isExists method error:`, error);
            global.gc();
        });
})();



module.exports = { getBitTransanctions }